package chloroform.json.comp

import java.util.Date
import chloroform.TypedProjectEntry
import chloroform.json._
import argo.jdom.{JsonRootNode, JsonNodeType, JsonNode}
import scaladuct.{Property, MutablePropertyValue, PropertyValue}

/**
 * User: zslajchrt
 * Date: 16.5.11
 * Time: 16:22
 */

object ComponentHandlers {

  def getConstructors =
    List[GenericFactoryForElement[PropertyValue[Nothing], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]]](
      new EnumFieldConstructor,
      new DateFieldConstructor,
      new ListFieldConstructor,
      new BooleanFieldConstructor,
      new NumberFieldConstructor,
      new StringFieldConstructor
    )

  def getUpdaters =
    List[GenericFactoryForElement[PropertyValue[Nothing], CookiePropertyProcessingContext, PropertyUpdater[AnyRef]]](
      new EnumFieldUpdater,
      new DateFieldUpdater,
      new ListFieldUpdater,
      new BooleanFieldUpdater,
      new NumberFieldUpdater,
      new StringFieldUpdater
    )

  def getHintConstructors =
    List[GenericFactoryForElement[Property[Nothing], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]]](
      new CheckMenuItemConstructor,
      new MenuItemConstructor)

  def getHintUpdaters = List[GenericFactoryForElement[Property[Nothing], CookiePropertyProcessingContext, PropertyUpdater[AnyRef]]](
    new MenuItemUpdater)

  def getProjectEntryConstructors = List[GenericFactoryForElement[TypedProjectEntry, ProjectEntriesBuildContext, JsonBuilderFactory[JsonRootNode]]](
    new ProjectEntryConstructor
  )

}

object NumberFieldUtils {

  def isNumberClass(clazz: Class[_]): Boolean = {
    return classOf[java.lang.Number].isAssignableFrom(clazz) ||
      clazz.getName() == "byte" ||
      clazz.getName() == "short" ||
      clazz.getName() == "int" ||
      clazz.getName() == "long" ||
      clazz.getName() == "float" ||
      clazz.getName() == "double"
  }

}

class NumberFieldConstructor
  extends GenericFactoryForElement[PropertyValue[Number], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(property: PropertyValue[Number], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    if (NumberFieldUtils.isNumberClass(property.clazz))
      Some(new NumberField(property.name, property.clazz.getName(), property.value.asInstanceOf[Number]))
    else
      None
  }

}

class NumberFieldUpdater
  extends GenericFactoryForElement[PropertyValue[Number], CookiePropertyProcessingContext, PropertyUpdater[Number]] {

  def create(property: PropertyValue[Number], ctx: CookiePropertyProcessingContext): Option[PropertyUpdater[Number]] = {
    if (NumberFieldUtils.isNumberClass(property.clazz))
      Some(new PropertyUpdater[Number] {
        def updateProperty(node: JsonNode) = {
          if (node.getType() == JsonNodeType.NUMBER)
            property.parse(node.getNumberValue())
          else
            property.parse(node.getStringValue())
        }
      })
    else
      None
  }
}


class StringFieldConstructor
  extends GenericFactoryForElement[PropertyValue[String], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(property: PropertyValue[String], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    if (classOf[java.lang.String] == property.clazz)
      Some(new StringField(property.name, property.value.asInstanceOf[String]))
    else
      None
  }

}

class StringFieldUpdater
  extends GenericFactoryForElement[PropertyValue[String], CookiePropertyProcessingContext, PropertyUpdater[String]] {

  def create(property: PropertyValue[String], ctx: CookiePropertyProcessingContext): Option[PropertyUpdater[String]] = {
    if (classOf[java.lang.String] == property.clazz)
      Some(new PropertyUpdater[String] {
        def updateProperty(node: JsonNode) = {
          property.parse(node.getStringValue())
        }
      })
    else
      None
  }
}

class EnumFieldConstructor
  extends GenericFactoryForElement[PropertyValue[_], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(property: PropertyValue[_], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    val isEnum: Boolean = property.clazz.isEnum()
    if (isEnum)
      Some(new StringField(property.name, "" + property.value))
    else
      None
  }

}

class EnumFieldUpdater
  extends GenericFactoryForElement[PropertyValue[AnyRef], CookiePropertyProcessingContext, PropertyUpdater[AnyRef]] {

  def create(property: PropertyValue[AnyRef], ctx: CookiePropertyProcessingContext): Option[PropertyUpdater[AnyRef]] = {
    if (property.clazz.isEnum())
      Some(new PropertyUpdater[AnyRef] {
        def updateProperty(node: JsonNode) = {
          property.parse(node.getStringValue())
        }
      })
    else
      None
  }
}

class BooleanFieldConstructor
  extends GenericFactoryForElement[PropertyValue[java.lang.Boolean], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(property: PropertyValue[java.lang.Boolean], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    if (classOf[java.lang.Boolean] == property.clazz || "boolean" == property.clazz.getName)
      Some(new BooleanField(property.name, property.value.asInstanceOf[Boolean]))
    else
      None
  }

}

class BooleanFieldUpdater
  extends GenericFactoryForElement[PropertyValue[java.lang.Boolean], CookiePropertyProcessingContext, PropertyUpdater[java.lang.Boolean]] {

  def create(property: PropertyValue[java.lang.Boolean], ctx: CookiePropertyProcessingContext): Option[PropertyUpdater[java.lang.Boolean]] = {
    if (classOf[java.lang.Boolean] == property.clazz || "boolean" == property.clazz.getName)
      Some(new PropertyUpdater[java.lang.Boolean] {
        def updateProperty(node: JsonNode) = {
          if (node.getType == JsonNodeType.TRUE) {
            property.update(java.lang.Boolean.TRUE)
          } else if (node.getType == JsonNodeType.FALSE)
            property.update(java.lang.Boolean.FALSE)
          else
            property.parse(node.getStringValue())
        }
      })
    else
      None
  }
}

class ListFieldConstructor
  extends GenericFactoryForElement[PropertyValue[java.util.List[_]], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(property: PropertyValue[java.util.List[_]], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    if (classOf[java.util.List[_]].isAssignableFrom(property.clazz)) {
      if (property.value != null) {
        Some(ArrayField(property.name, property.value.asInstanceOf[java.util.List[String]]))
      } else {
        Some(new ArrayField(property.name, Nil))
      }
    }
    else
      None
  }

}

class ListFieldUpdater
  extends GenericFactoryForElement[PropertyValue[java.util.List[_]], CookiePropertyProcessingContext, PropertyUpdater[java.util.List[_]]] {

  def create(property: PropertyValue[java.util.List[_]], ctx: CookiePropertyProcessingContext): Option[PropertyUpdater[java.util.List[_]]] = {
    if (classOf[java.util.List[_]].isAssignableFrom(property.clazz)) {
      Some(new PropertyUpdater[java.util.List[_]] {
        def updateProperty(node: JsonNode) = {

          def convertToListOfStrings(nodes: java.util.List[JsonNode]): java.util.List[String] = {
            val result = new java.util.ArrayList[String]()
            val iter = nodes.iterator()

            while (iter.hasNext) {
              val node = iter.next()
              if (node.getType == JsonNodeType.NUMBER)
                result.add(node.getNumberValue())
              else
                result.add(node.getStringValue())
            }
            result
          }

          if (node.getType == JsonNodeType.STRING)
            property.parse(node.getStringValue())
          else {
            val listOfStrings = convertToListOfStrings(node.getArrayNode())
            property.parse(listOfStrings)
          }

        }
      })
    } else
      None
  }
}

class DateFieldConstructor
  extends GenericFactoryForElement[PropertyValue[Date], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(property: PropertyValue[Date], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    if (classOf[Date].isAssignableFrom(property.clazz))
      Some(new DateField(property.name, property.value.asInstanceOf[Date]))
    else
      None
  }

}

class DateFieldUpdater
  extends GenericFactoryForElement[PropertyValue[Date], CookiePropertyProcessingContext, PropertyUpdater[Date]] {

  def create(property: PropertyValue[Date], ctx: CookiePropertyProcessingContext): Option[PropertyUpdater[Date]] = {
    if (classOf[Date].isAssignableFrom(property.clazz))
      Some(new PropertyUpdater[Date] {
        def updateProperty(node: JsonNode) = {
          property.parse(node.getStringValue())
        }
      })
    else
      None
  }
}

class MenuItemConstructor
  extends GenericFactoryForElement[Property[_], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(hint: Property[_], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] =
    Some(new Item(hint.name, "" + hint.value))

}

object CheckMenuItemUtils {
  def isCheckedHint(hint: Property[_]): Boolean = {
    hint.isInstanceOf[MutablePropertyValue[_]] &&
      (hint.clazz == classOf[java.lang.Boolean] || hint.clazz.getName == "boolean")
  }
}

class CheckMenuItemConstructor
  extends GenericFactoryForElement[Property[java.lang.Boolean], CookiePropertyProcessingContext, JsonBuilderFactory[JsonRootNode]] {

  def create(hint: Property[java.lang.Boolean], ctx: CookiePropertyProcessingContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    if (CheckMenuItemUtils.isCheckedHint(hint))
      Some(new CheckItem(hint.name, hint.name) {
        this.checked <= hint.value.asInstanceOf[Boolean]
      })
    else
      None
  }

}

class MenuItemUpdater
  extends GenericFactoryForElement[Property[Nothing], CookiePropertyProcessingContext, PropertyUpdater[AnyRef]] {

  def create(hint: Property[Nothing], ctx: CookiePropertyProcessingContext): Option[PropertyUpdater[AnyRef]] = {
    Some(new PropertyUpdater[AnyRef] {

      def updateProperty(node: JsonNode): Property[AnyRef] = {
        if (CheckMenuItemUtils.isCheckedHint(hint)) {
          val mutableHint = hint.asInstanceOf[MutablePropertyValue[java.lang.Boolean]]

          if (node.getType == JsonNodeType.TRUE)
            mutableHint.update(java.lang.Boolean.TRUE)
          else if (node.getType == JsonNodeType.FALSE)
            mutableHint.update(java.lang.Boolean.FALSE)
          else
            mutableHint.parse(node.getStringValue())

          mutableHint

        } else {
          hint
        }
      }
    })
  }
}

class ProjectEntryConstructor
  extends GenericFactoryForElement[TypedProjectEntry, ProjectEntriesBuildContext, JsonBuilderFactory[JsonRootNode]] {

  def create(projectEntry: TypedProjectEntry, ctx: ProjectEntriesBuildContext): Option[JsonBuilderFactory[JsonRootNode]] = {
    val entryType = projectEntry.getEntryType()
    val entryTypeName = if (entryType.isInstanceOf[Class[_]])
      entryType.asInstanceOf[Class[_]].getName
    else
      "" + entryType

    val extsMap: List[JsonBuilderFactory[JsonRootNode]] = if (projectEntry.extensions().isEmpty)
      Nil
    //Map.empty[String, JsonBuilderFactory[JsonNode]]
    else
      ctx.jsonHandler.getBuildersForProjectEntries(ctx.projectGateway, projectEntry.extensions().asInstanceOf[List[TypedProjectEntry]])

    Some(new ProjectEntry(projectEntry.fullName, entryTypeName, extsMap))
  }

}
